package com.zbs.framework.dao.function.service.impl;

import com.zbs.framework.common.utils.ErrorAssert;
import com.zbs.framework.dao.base.TreeNodeHelper;
import com.zbs.framework.dao.function.entity.Function;
import com.zbs.framework.dao.function.entity.FunctionPermission;
import com.zbs.framework.dao.function.repository.FunctionPermissionRepository;
import com.zbs.framework.dao.function.repository.FunctionRepository;
import com.zbs.framework.dao.function.service.FunctionService;
import com.zbs.framework.dao.function.service.model.FunctionNode;
import com.zbs.framework.dao.function.service.model.PermissionBasic;
import com.zbs.framework.dao.permission.entity.Permission;
import com.zbs.framework.dao.permission.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

/**
 * Created by Zbs
 * 2019-07-08 15:02
 */
@Service
@Transactional
public class FunctionServiceImpl implements FunctionService {

    @Autowired
    private FunctionRepository functionRepository;

    @Autowired
    private FunctionPermissionRepository funcPermRepository;

    @Autowired
    private PermissionService permissionService;

    @PersistenceContext
    private EntityManager em;

    @Override
    
    public Function save(Long parentId, String identifier, String name,
                         String description, List<Long> permIds) {
        // 参数检查
        ErrorAssert.hasLength(identifier, "Identifier can not be empty!");
        ErrorAssert.hasLength(name, "Name can not be empty!");
        // 查重
        ErrorAssert.isTrue(isIdentifierAva(identifier), "Duplicated identifier!");
        // 父级检查
        Function parent = null;
        if (parentId != null) {
            parent = functionRepository.findById(parentId).orElse(null);
            ErrorAssert.notNull(parent, "Parent not found!");
        }
        // 获取编码
        String code = TreeNodeHelper.getNewCode(em, Function.class, parent);
        Integer level = (parent == null) ? 1 : parent.getLevel() + 1;
        // 新增
        Function function = new Function(code, parentId, level, identifier,
                name, description);
        functionRepository.save(function);

        // 添加功能权限关联关系
        addFuncPerms(function.getId(), permIds);

        return function;
    }

    /**
     * 添加功能与权限关联关系
     *
     * @param funcId
     * @param permIds
     */
    private void addFuncPerms(Long funcId, List<Long> permIds) {
        if (CollectionUtils.isEmpty(permIds)) return;
        permIds.forEach(permId -> funcPermRepository.save(new FunctionPermission(funcId, permId)));
    }

    @Override
    
    public void edit(Long id, String identifier, String name, String description, List<Long> permIds) {
        // 参数检查
        ErrorAssert.notNull(id, "Id can not be null!");
        ErrorAssert.hasLength(identifier, "Identifier can not be empty!");
        ErrorAssert.hasLength(name, "Name can not be empty!");
        // 检查是否存在
        Function function = functionRepository.findById(id).orElse(null);
        ErrorAssert.notNull(function, "Function not exist!");
        if (!identifier.equals(function.getIdentifier())) {
            // 标识符发生变更，查重
            ErrorAssert.isTrue(isIdentifierAva(identifier), "Duplicated identifier!");
            // 修改标识符
            function.setIdentifier(identifier);
        }
        // name
        function.setName(name);
        // description
        function.setDescription(description);
        functionRepository.save(function);

        // 删除原有功能与权限关联关系
        funcPermRepository.deleteAllByFunctionId(id);
        // 添加功能与权限关联关系
        addFuncPerms(id, permIds);
    }

    @Override
    
    public void delete(Long id) {
        functionRepository.deleteById(id);
    }

    @Override
    public List<FunctionNode> getTree(Long rootId) {
        List<Function> parents;
        List<Function> descendants;
        if (rootId == null) {
            // 获取整棵树
            parents = findAll(1, null, null, Sort.by("treeCode"));
            descendants = findAll(null, 1, null, Sort.by("treeCode"));
        } else {
            // 获取子树
            Function root = functionRepository.findById(rootId).orElse(null);
            ErrorAssert.notNull(root, String.format("Function not found, id = %s", rootId));
            // 获取所有后代节点
            String fuzzCode = root.getTreeCode().substring(0, 2 * root.getLevel()) + "%";
            descendants = findAll(null, null, fuzzCode, Sort.by("treeCode"));
            //
            parents = new ArrayList<>();
            parents.add(root);
        }
        return recursionChildren(parents, descendants, buildFuncPermsMap());
    }

    private Map<Long, Set<PermissionBasic>> buildFuncPermsMap() {
        Map<Long, Set<PermissionBasic>> map = new HashMap<>();
        List<FunctionPermission> fps = funcPermRepository.findAll();
        if (!CollectionUtils.isEmpty(fps)) {
            Map<Long, Permission> pMap = buildPermBasicMap();
            for (FunctionPermission fp : fps) {
                Permission p = pMap.get(fp.getPermissionId());
                if (p == null) continue;
                if (map.containsKey(fp.getFunctionId())) {
                    map.get(fp.getFunctionId()).add(new PermissionBasic(p));
                } else {
                    Set<PermissionBasic> permSet = new HashSet<>();
                    permSet.add(new PermissionBasic(p));
                    map.put(fp.getFunctionId(), permSet);
                }
            }
        }
        return map;
    }

    private Map<Long, Permission> buildPermBasicMap() {
        Map<Long, Permission> map = new HashMap<>();
        // 查询所有权限基础信息
        List<Permission> perms = permissionService.findAllPerms();
        if (!CollectionUtils.isEmpty(perms)) {
            perms.forEach(p -> map.put(p.getId(), p));
        }
        return map;
    }

    private List<Function> findAll(Integer level, Integer levelGT, String fuzzCode, Sort sort) {
        Specification<Function> specification = new Specification<Function>() {
            @Nullable
            @Override
            public Predicate toPredicate(Root<Function> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                List<Predicate> list = new ArrayList<>();
                // 等级
                if (level != null){
                    list.add(cb.equal(root.get("level"), level));
                }
                // 等级条件
                if (levelGT != null){
                    list.add(cb.greaterThan(root.get("level"), levelGT));
                }
                // 模糊code
                if (StringUtils.hasLength(fuzzCode)){
                    list.add(cb.like(root.get("treeCode"), fuzzCode));
                }
                return cb.and(list.toArray(new Predicate[]{}));
            }
        };
        if (sort == null) sort = Sort.unsorted();
        return functionRepository.findAll(specification, sort);
    }

    /**
     * 递归子分类数据
     * @param parents
     * @param from
     * @return
     */
    private List<FunctionNode> recursionChildren(List<Function> parents, List<Function> from, Map<Long, Set<PermissionBasic>> fpMap) {
        List<FunctionNode> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(parents)){
            parents.forEach(c -> {
                List<Function> children = getChildren(c, from);
                // 递归
                FunctionNode scn = new FunctionNode(c, fpMap.get(c.getId()), recursionChildren(children, from, fpMap));
                list.add(scn);
            });
        }
        return list;
    }

    /**
     * 筛选子级数据
     * @param parent
     * @param from
     * @return
     */
    private List<Function> getChildren(Function parent, List<Function> from) {
        List<Function> list = new ArrayList<>();
        if (!CollectionUtils.isEmpty(from)){
            from.forEach(c -> {
                if (parent.getId().equals(c.getParentId())) list.add(c);
            });
        }
        return list;
    }

    /**
     * 检查标识符是否可用
     *
     * @param identifier
     * @return
     */
    private boolean isIdentifierAva(String identifier) {
        List<Function> exist = functionRepository.findByIdentifier(identifier);
        return CollectionUtils.isEmpty(exist);
    }
}
